package org.hcyspartnoc.luoxin.service.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.baomidou.mybatisplus.extension.toolkit.Db;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import jakarta.annotation.Resource;
import org.hcyspartnoc.luoxin.common.core.AjaxResult;
import org.hcyspartnoc.luoxin.common.core.PageBean;
import org.hcyspartnoc.luoxin.common.core.context.BaseContextHolder;
import org.hcyspartnoc.luoxin.common.enums.chat.ChatMessageStatusEnum;
import org.hcyspartnoc.luoxin.common.enums.chat.ChatMessageTypeEnum;
import org.hcyspartnoc.luoxin.common.enums.service.OrderTypeEnum;
import org.hcyspartnoc.luoxin.common.enums.service.ServiceCounselorStatusEnum;
import org.hcyspartnoc.luoxin.common.enums.user.UserContactTypeEnum;
import org.hcyspartnoc.luoxin.common.exception.asset.AssetVCounselorException;
import org.hcyspartnoc.luoxin.common.exception.framework.AuthException;
import org.hcyspartnoc.luoxin.common.exception.service.ServiceCounselorOrderException;
import org.hcyspartnoc.luoxin.common.exception.service.WeiXinException;
import org.hcyspartnoc.luoxin.mapper.asset.AssetVirtualCounselorMapper;
import org.hcyspartnoc.luoxin.mapper.chat.ChatCounselingSessionMapper;
import org.hcyspartnoc.luoxin.mapper.chat.ChatMessageMapper;
import org.hcyspartnoc.luoxin.mapper.service.ServiceCounselorOrderMapper;
import org.hcyspartnoc.luoxin.mapper.service.ServicePaymentMapper;
import org.hcyspartnoc.luoxin.pojo.dto.service.ServiceCounselorOrderAddDto;
import org.hcyspartnoc.luoxin.pojo.po.asset.AssetVirtualCounselorPo;
import org.hcyspartnoc.luoxin.pojo.po.chat.ChatCounselingSessionPo;
import org.hcyspartnoc.luoxin.pojo.po.chat.ChatMessagePo;
import org.hcyspartnoc.luoxin.pojo.po.service.ServiceCounselorOrderPo;
import org.hcyspartnoc.luoxin.pojo.po.service.ServicePaymentPo;
import org.hcyspartnoc.luoxin.pojo.po.system.SysUserPo;
import org.hcyspartnoc.luoxin.pojo.po.user.UserContactPo;
import org.hcyspartnoc.luoxin.pojo.vo.chat.ChatCommonVo;
import org.hcyspartnoc.luoxin.pojo.vo.service.ServiceCounselorOrderVo;
import org.hcyspartnoc.luoxin.pojo.vo.service.WxPayRespVO;
import org.hcyspartnoc.luoxin.service.service.IServiceCounselorOrderService;
import org.hcyspartnoc.luoxin.utils.*;
import org.hcyspartnoc.luoxin.utils.redis.RedisComponent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.text.DecimalFormat;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Objects;

@Service
public class ServiceCounselorOrderServiceImpl extends ServiceImpl<ServiceCounselorOrderMapper, ServiceCounselorOrderPo>
        implements IServiceCounselorOrderService {
    @Resource
    private AssetVirtualCounselorMapper assetVirtualCounselorMapper;
    @Resource
    private WeiXinUtils weiXinUtils;
    @Resource
    private ServicePaymentMapper servicePaymentMapper;
    @Resource
    private ServiceCounselorOrderMapper serviceCounselorOrderMapper;
    @Resource
    private ChatCounselingSessionMapper chatCounselingSessionMapper;
    @Resource
    private LuoxinUtils luoxinUtils;
    private static final ArrayList<Integer> DISABLE_CREATE_STATUS = new ArrayList<>();

    static {
        DISABLE_CREATE_STATUS.add(ServiceCounselorStatusEnum.NO_PAY.getCode());
        DISABLE_CREATE_STATUS.add(ServiceCounselorStatusEnum.IN_PROGRESS.getCode());
        DISABLE_CREATE_STATUS.add(ServiceCounselorStatusEnum.SCHEDULED.getCode());
    }

    @Resource
    private ChannelContextUtils channelContextUtils;
    @Resource
    private RedisComponent redisComponent;
    @Autowired
    private ChatMessageMapper chatMessageMapper;

    @Override
    @Transactional
    public ServiceCounselorOrderVo createCOrder(ServiceCounselorOrderAddDto serviceCounselorOrderAddDto) throws AssetVCounselorException, ServiceCounselorOrderException, WeiXinException {
        // 如果预约时间不正确，抛出异常
        LocalDateTime scheduledTime = StringUtils.parseDateTime(serviceCounselorOrderAddDto.getScheduledTimestamp());
        if (scheduledTime.isBefore(LocalDateTime.now()))
            throw new ServiceCounselorOrderException(ServiceCounselorOrderException.TIME_ERROR);
        // 检查咨询师是否存在
        char counselorType = serviceCounselorOrderAddDto.getCounselorId().charAt(0); // 咨询师类型
        int counselorId;// 咨询师id
        try {
            counselorId = Integer.parseInt(serviceCounselorOrderAddDto.getCounselorId().substring(1));
        } catch (Exception ignore) {
            throw new AssetVCounselorException(AssetVCounselorException.VIRTUAL_COUNSELOR_NOT_EXITS);
        }
        ServiceCounselorOrderPo counselorOrderPo;
        switch (counselorType) {
            case 'V':
                counselorOrderPo = createVirtualOrder(serviceCounselorOrderAddDto, counselorId);
                break;
            case 'R':
                // TODO
                throw new AssetVCounselorException(AssetVCounselorException.VIRTUAL_COUNSELOR_NOT_EXITS);
            default:
                throw new AssetVCounselorException(AssetVCounselorException.VIRTUAL_COUNSELOR_NOT_EXITS);
        }
        if (Objects.nonNull(counselorOrderPo.getIsExists()) && counselorOrderPo.getIsExists()) {
            ServicePaymentPo servicePaymentPo = servicePaymentMapper.selectOne(new LambdaQueryWrapper<>(ServicePaymentPo.class)
                    .eq(ServicePaymentPo::getOrderId, counselorOrderPo.getId()));
            ServiceCounselorOrderVo serviceCounselorOrderVo = ServiceCounselorOrderPoToVo(counselorOrderPo);
            serviceCounselorOrderVo.setWxPayRespVO(BeanUtil.copyProperties(servicePaymentPo, WxPayRespVO.class));
            return serviceCounselorOrderVo;
        }
        // 设置数据
        counselorOrderPo.setUserId(BaseContextHolder.getContext().getBaseCurrentUser().getId());
        counselorOrderPo.setServiceType(serviceCounselorOrderAddDto.getServiceTypeE().getCode());
        counselorOrderPo.setStatus(ServiceCounselorStatusEnum.NO_PAY.getCode());
        counselorOrderPo.setScheduledTime(scheduledTime);

        // 设置id（随机uuid）
        String randomId = StringUtils.randomNumStr(19);
        while (randomId.charAt(0) == '0')
            randomId = StringUtils.randomNumStr(19);
        counselorOrderPo.setId(Long.parseLong(randomId));
        // 尝试创建微信预支付
        WxPayRespVO wxPayRespVO = weiXinUtils.createOrder((int) (counselorOrderPo.getServicePrice() * 100), randomId, OrderTypeEnum.COUNSELOR);
        // 存入数据库
        try {
            // 存入订单
            save(counselorOrderPo);
            // 存入支付信息
            ServicePaymentPo servicePaymentPo = BeanUtil.copyProperties(wxPayRespVO, ServicePaymentPo.class);
            servicePaymentPo.setOrderId(Long.parseLong(randomId))
                    .setOrderType(OrderTypeEnum.COUNSELOR.getCode());
            Db.save(servicePaymentPo);
        } catch (Exception ignore) {
            // 订单创建异常 此验证主要是防止randomId生成重复
            throw new ServiceCounselorOrderException(ServiceCounselorOrderException.ORDER_CREATE_ERROR);
        }
        ServiceCounselorOrderVo serviceCounselorOrderVo = ServiceCounselorOrderPoToVo(getById(Long.parseLong(randomId)));
        serviceCounselorOrderVo.setWxPayRespVO(wxPayRespVO);
        return serviceCounselorOrderVo;
    }

    @Override
    @Transactional
    public void cancelOrder(Long orderId) throws ServiceCounselorOrderException, AuthException {
        // 查询订单是否存在
        ServiceCounselorOrderPo orderPo = orderNotExistsThrow(orderId);
        // 查询订单是否是当前用户的创建的
        SysUserPo currentUser = BaseContextHolder.getContext().getBaseCurrentUser();
        if (!orderPo.getUserId().equals(currentUser.getId()))
            throw new AuthException(AuthException.PERMISSION_NOT_ENOUGH);
        // 查询订单是否是未支付状态
        if (!orderPo.getStatus().equals(ServiceCounselorStatusEnum.NO_PAY.getCode()))
            throw new ServiceCounselorOrderException(ServiceCounselorOrderException.ORDER_STATUS_ERROR);
        // 取消订单<-查询当前订单对应的支付参数
        ServicePaymentPo servicePaymentPo = servicePaymentMapper.selectOne(new LambdaQueryWrapper<>(ServicePaymentPo.class)
                .eq(ServicePaymentPo::getOrderId, orderId));
        if (Objects.isNull(servicePaymentPo))
            throw new ServiceCounselorOrderException(ServiceCounselorOrderException.ORDER_NOT_EXISTS);
        if (!weiXinUtils.closeOrder(servicePaymentPo))
            throw new ServiceCounselorOrderException(ServiceCounselorOrderException.ORDER_CANCEL_FAIL);
        // 修改状态
        serviceCounselorOrderMapper.update(new LambdaUpdateWrapper<>(ServiceCounselorOrderPo.class)
                .eq(ServiceCounselorOrderPo::getId, orderId)
                .eq(ServiceCounselorOrderPo::getStatus, ServiceCounselorStatusEnum.NO_PAY.getCode())
                .set(ServiceCounselorOrderPo::getStatus, ServiceCounselorStatusEnum.CANCELLED.getCode()));
    }

    @Override
    public PageBean<ServiceCounselorOrderPo, ServiceCounselorOrderVo> queryPageByToken(Integer currentPage, Integer pageSize, Integer status) {
        MPJLambdaWrapper<ServiceCounselorOrderPo> eq = new MPJLambdaWrapper<ServiceCounselorOrderPo>()
                .eq(status != 0, ServiceCounselorOrderPo::getStatus, status)
                .eq(ServiceCounselorOrderPo::getUserId, BaseContextHolder.getContext().getBaseCurrentUser().getId());
        Page<ServiceCounselorOrderPo> page = new Page<>(currentPage, pageSize);
        serviceCounselorOrderMapper.selectPage(page, eq);
        ArrayList<ServiceCounselorOrderVo> serviceCounselorOrderVos = new ArrayList<>();
        for (ServiceCounselorOrderPo po : page.getRecords()) {
            serviceCounselorOrderVos.add(ServiceCounselorOrderPoToVo(po));
        }
        return new PageBean<>(page, serviceCounselorOrderVos);
    }

    @Override
    public ServiceCounselorOrderVo queryById(Long orderId) throws AuthException, ServiceCounselorOrderException {
        // 查询订单是否存在
        ServiceCounselorOrderPo orderPo = orderNotExistsThrow(orderId);
        // 查询订单是否是当前用户的创建的
        SysUserPo currentUser = BaseContextHolder.getContext().getBaseCurrentUser();
        if (!orderPo.getUserId().equals(currentUser.getId()))
            throw new AuthException(AuthException.PERMISSION_NOT_ENOUGH);
        // 查询订单状态，如果为未支付则返回订单支付码
        ServiceCounselorOrderVo serviceCounselorOrderVo = ServiceCounselorOrderPoToVo(orderPo);
        if (orderPo.getStatus().equals(ServiceCounselorStatusEnum.NO_PAY.getCode())) {
            ServicePaymentPo servicePaymentPo = servicePaymentMapper.selectOne(new LambdaQueryWrapper<>(ServicePaymentPo.class)
                    .eq(ServicePaymentPo::getOrderId, orderId));
            serviceCounselorOrderVo.setWxPayRespVO(BeanUtil.copyProperties(servicePaymentPo, WxPayRespVO.class));
        }
        return serviceCounselorOrderVo;
    }

    @Override
    @Transactional
    public void startOrder(Long orderId) throws ServiceCounselorOrderException, AuthException {
        // 查询订单是否存在
        ServiceCounselorOrderPo orderPo = orderNotExistsThrow(orderId);
        // 查询订单是否是当前用户的创建的
        SysUserPo currentUser = BaseContextHolder.getContext().getBaseCurrentUser();
        if (!orderPo.getUserId().equals(currentUser.getId()))
            throw new AuthException(AuthException.PERMISSION_NOT_ENOUGH);
        // 查询订单状态，如果为已支付且咨询目标为虚拟咨询师则可以开启，为真人咨询师则不可以手动开启
        if (!Objects.equals(orderPo.getCounselorId().charAt(0), 'V'))
            throw new ServiceCounselorOrderException(ServiceCounselorOrderException.ORDER_COUNT_NOT_START);
        // 状态为已预约才可以开启
        if (!orderPo.getStatus().equals(ServiceCounselorStatusEnum.SCHEDULED.getCode()))
            throw new ServiceCounselorOrderException(ServiceCounselorOrderException.ORDER_STATUS_ERROR);
        // 计算sessionId
        String sessionId = LuoxinUtils.generateSessionId('U' + currentUser.getId().toString(), orderPo.getCounselorId());
        // 开启一个个咨询会话
        ChatCounselingSessionPo chatCounselingSessionPo = new ChatCounselingSessionPo();
        chatCounselingSessionPo.setUserId(currentUser.getId());
        chatCounselingSessionPo.setCounselorId(orderPo.getCounselorId());
        chatCounselingSessionPo.setMasterSessionId(sessionId);
        chatCounselingSessionPo.setStatus(true);
        Db.save(chatCounselingSessionPo);
        // 开启订单并将会话id和订单绑定
        orderPo.setStatus(ServiceCounselorStatusEnum.IN_PROGRESS.getCode())
                .setStartTime(LocalDateTime.now())
                .setSessionId(chatCounselingSessionPo.getId());
        saveOrUpdate(orderPo);
        // 将开启新订单的消息记录
        ChatMessagePo chatMessagePo = new ChatMessagePo();
        chatMessagePo.setMessageContent(chatCounselingSessionPo.getId().toString())
                .setMessageType(ChatMessageTypeEnum.ORDER_SESSION_START.getCode())
                .setStatus(ChatMessageStatusEnum.HAS_SEND.getCode())
                .setSessionId(sessionId);
        Db.save(chatMessagePo);
        // 将今天发送过的主会话消息同步给子会话
        // 获取主会话记忆体消息，并将其设置为子会话的记忆体
        String chatVCMemory = redisComponent.getChatVCMemory(sessionId);
        redisComponent.setChatOrderVcMemory(chatCounselingSessionPo.getId().toString(), chatVCMemory);
        // 通过ws发送消息到前端
        ChatCommonVo chatCommonVo = new ChatCommonVo();
        chatCommonVo.setMessageContent(ServiceCounselorOrderPoToVo(orderPo))
                .setMessageType(ChatMessageTypeEnum.ORDER_SESSION_START)
                .setSessionId(sessionId);
        channelContextUtils.sendToUser(currentUser.getId().toString(), JSON.toJSONString(AjaxResult.success(chatCommonVo)));
    }

    private ServiceCounselorOrderPo createVirtualOrder(ServiceCounselorOrderAddDto serviceCounselorOrderAddDto, Integer counselorId) throws AssetVCounselorException, ServiceCounselorOrderException {
        // 获取当前用户是否拥有该虚拟咨询师联系人
        MPJLambdaWrapper<AssetVirtualCounselorPo> eq = new MPJLambdaWrapper<AssetVirtualCounselorPo>()
                .leftJoin(UserContactPo.class, UserContactPo::getContactId, AssetVirtualCounselorPo::getId)
                .eq(UserContactPo::getUserId, BaseContextHolder.getContext().getBaseCurrentUser().getId())
                .eq(UserContactPo::getContactType, UserContactTypeEnum.V_COUNSELOR.getCode())
                .eq(AssetVirtualCounselorPo::getId, counselorId);
        AssetVirtualCounselorPo assetVirtualCounselorPo = assetVirtualCounselorMapper.selectJoinOne(AssetVirtualCounselorPo.class, eq);
        if (Objects.isNull(assetVirtualCounselorPo))
            throw new AssetVCounselorException(AssetVCounselorException.VIRTUAL_COUNSELOR_NOT_EXITS);
        // 查询当前咨询师是否有进行中的或者预约中的订单
        ServiceCounselorOrderPo savedOrder = serviceCounselorOrderMapper.selectOne(new LambdaQueryWrapper<>(ServiceCounselorOrderPo.class)
                .eq(ServiceCounselorOrderPo::getUserId, BaseContextHolder.getContext().getBaseCurrentUser().getId())
                .eq(ServiceCounselorOrderPo::getCounselorId, 'V' + counselorId.toString())
                .in(ServiceCounselorOrderPo::getStatus, DISABLE_CREATE_STATUS));
        if (Objects.nonNull(savedOrder)) {
            savedOrder.setIsExists(true);
            return savedOrder;
//            throw new ServiceCounselorOrderException(ServiceCounselorOrderException.HAS_NOT_USE_ORDER);
        }
        // 计算价格
        double hour = serviceCounselorOrderAddDto.getServiceDuration() / 60.0;
        String formatPrice = new DecimalFormat("#.00").format(assetVirtualCounselorPo.getHourPrice() * hour);
        double totalPrice = Double.parseDouble(formatPrice);
        // 最低价为0.01
        totalPrice = Math.max(totalPrice, 0.01);
        ServiceCounselorOrderPo serviceCounselorOrderPo = BeanUtil.copyProperties(serviceCounselorOrderAddDto, ServiceCounselorOrderPo.class);
        serviceCounselorOrderPo.setServicePrice(totalPrice);
        return serviceCounselorOrderPo;
    }

    public static ServiceCounselorOrderVo ServiceCounselorOrderPoToVo(ServiceCounselorOrderPo serviceCounselorOrderPo) {
        ServiceCounselorOrderVo serviceCounselorOrderVo = BeanUtil.copyProperties(serviceCounselorOrderPo, ServiceCounselorOrderVo.class);
        serviceCounselorOrderVo.setFormatEndTime(StringUtils.formatDateTime(serviceCounselorOrderPo.getEndTime()));
        serviceCounselorOrderVo.setFormatScheduledTime(StringUtils.formatDateTime(serviceCounselorOrderPo.getScheduledTime()));
        serviceCounselorOrderVo.setFormatCreatedAt(StringUtils.formatDateTime(serviceCounselorOrderPo.getCreatedAt()));
        serviceCounselorOrderVo.setFormatUpdatedAt(StringUtils.formatDateTime(serviceCounselorOrderPo.getUpdatedAt()));
        serviceCounselorOrderVo.setFormatStartTime(StringUtils.formatDateTime(serviceCounselorOrderPo.getStartTime()));
        return serviceCounselorOrderVo;
    }

    private ServiceCounselorOrderPo orderNotExistsThrow(Long orderId) throws ServiceCounselorOrderException {
        ServiceCounselorOrderPo po = getById(orderId);
        if (Objects.isNull(po))
            throw new ServiceCounselorOrderException(ServiceCounselorOrderException.ORDER_NOT_EXISTS);
        return po;
    }


}
